using System;
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.WowAddonStudio.FrameXml.Components;
using Microsoft.WowAddonStudio.FrameXml.Serialization;
using Microsoft.WowAddonStudio.FrameXml.Services;

namespace Microsoft.WowAddonStudio.FrameXml.Design.Serialization
{
	/// <summary>
	/// Provides class for serializing a set of components or serializable objects into a serialization store.
	/// </summary>
	[Guid("6E83DAA6-AD60-4629-8882-FC3E0F838F7D")]
	public sealed class FrameXmlComponentSerializationService : ComponentSerializationService, IFrameXmlComponentSerializationService
	{
		private readonly IServiceProvider serviceProvider;
		private readonly IFrameXmlSerializer serializer;

		/// <summary>
		/// Initializes a new instance of the <see cref="FrameXmlComponentSerializationService"/> class.
		/// </summary>
		/// <param name="serviceProvider">The service provider.</param>
		/// <param name="serializer">The serializer.</param>
		public FrameXmlComponentSerializationService(IServiceProvider serviceProvider, IFrameXmlSerializer serializer)
		{
			if (serializer == null)
				throw new ArgumentNullException("serializer");
			if (serviceProvider == null)
				throw new ArgumentNullException("serviceProvider");

			this.serviceProvider = serviceProvider;
			this.serializer = serializer;
		}

		/// <summary>
		/// Creates a new <see cref="T:System.ComponentModel.Design.Serialization.SerializationStore"/>.
		/// </summary>
		/// <returns>
		/// A new <see cref="T:System.ComponentModel.Design.Serialization.SerializationStore"/>.
		/// </returns>
		public override SerializationStore CreateStore()
		{
			return new FrameXmlSerializationStore(serviceProvider);
		}

		/// <summary>
		/// Deserializes the given store to produce a collection of objects.
		/// </summary>
		/// <param name="store">The <see cref="T:System.ComponentModel.Design.Serialization.SerializationStore"/> to deserialize.</param>
		/// <returns>
		/// A collection of objects created according to the stored state.
		/// </returns>
		/// <exception cref="T:System.ArgumentNullException">
		/// 	<paramref name="store"/> is null.</exception>
		/// <exception cref="T:System.InvalidOperationException">
		/// 	<paramref name="store"/> does not contain data in a format the serialization container can process.</exception>
		public override ICollection Deserialize(SerializationStore store)
		{
			return Deserialize(store, null);
		}

		/// <summary>
		/// Deserializes the given store and populates the given <see cref="T:System.ComponentModel.IContainer"/> with deserialized <see cref="T:System.ComponentModel.IComponent"/> objects.
		/// </summary>
		/// <param name="store">The <see cref="T:System.ComponentModel.Design.Serialization.SerializationStore"/> to deserialize.</param>
		/// <param name="container">The <see cref="T:System.ComponentModel.IContainer"/> to which <see cref="T:System.ComponentModel.IComponent"/> objects will be added.</param>
		/// <returns>
		/// A collection of objects created according to the stored state.
		/// </returns>
		/// <exception cref="T:System.ArgumentNullException">
		/// 	<paramref name="store"/> or <paramref name="container"/> is null.</exception>
		/// <exception cref="T:System.InvalidOperationException">
		/// 	<paramref name="store"/> does not contain data in a format the serialization container can process.</exception>
		public override ICollection Deserialize(SerializationStore store, IContainer container)
		{
			if (store == null)
				throw new ArgumentNullException("store");

			var serializationStore = store as FrameXmlSerializationStore;
			if (serializationStore == null) throw new BadSerializationStoreException(store);

			return serializationStore.Deserialize(serviceProvider, container, serializer);
		}

		/// <summary>
		/// Deserializes the given <see cref="T:System.ComponentModel.Design.Serialization.SerializationStore"/> to the given container, optionally applying default property values.
		/// </summary>
		/// <param name="store">The <see cref="T:System.ComponentModel.Design.Serialization.SerializationStore"/> to deserialize.</param>
		/// <param name="container">The container to which <see cref="T:System.ComponentModel.IComponent"/> objects will be added.</param>
		/// <param name="validateRecycledTypes">true to guarantee that the deserialization will only work if applied to an object of the same type.</param>
		/// <param name="applyDefaults">true to indicate that the default property values should be applied.</param>
		/// <exception cref="T:System.ArgumentNullException">
		/// 	<paramref name="store"/> or <paramref name="container"/> is null.</exception>
		/// <exception cref="T:System.InvalidOperationException">
		/// 	<paramref name="store"/> does not contain data in a format the serialization container can process.</exception>
		public override void DeserializeTo(SerializationStore store, IContainer container, bool validateRecycledTypes,
		                                   bool applyDefaults)
		{
			Deserialize(store, container);
		}

		/// <summary>
		/// Loads a <see cref="T:System.ComponentModel.Design.Serialization.SerializationStore"/> from a stream.
		/// </summary>
		/// <param name="stream">The <see cref="T:System.IO.Stream"/> from which the store will be loaded.</param>
		/// <returns>
		/// A new <see cref="T:System.ComponentModel.Design.Serialization.SerializationStore"/> instance.
		/// </returns>
		/// <exception cref="T:System.ArgumentNullException">
		/// 	<paramref name="stream"/> is null.</exception>
		/// <exception cref="T:System.InvalidOperationException">
		/// 	<paramref name="stream"/> does not contain data saved by a previous call to <see cref="M:System.ComponentModel.Design.Serialization.SerializationStore.Save(System.IO.Stream)"/>.</exception>
		public override SerializationStore LoadStore(Stream stream)
		{
			if (stream == null)
				throw new ArgumentNullException("stream");

			return FrameXmlSerializationStore.Load(stream);
		}

		/// <summary>
		/// Serializes the given object to the given <see cref="T:System.ComponentModel.Design.Serialization.SerializationStore"/>.
		/// </summary>
		/// <param name="store">The <see cref="T:System.ComponentModel.Design.Serialization.SerializationStore"/> to which the state of <paramref name="value"/> will be written.</param>
		/// <param name="value">The object to serialize.</param>
		/// <exception cref="T:System.ArgumentNullException">
		/// 	<paramref name="store"/> or <paramref name="value"/> is null.</exception>
		/// <exception cref="T:System.InvalidOperationException">
		/// 	<paramref name="store"/> is closed, or <paramref name="store"/> is not a supported type of serialization store. Use a store returned by <see cref="M:System.ComponentModel.Design.Serialization.CodeDomComponentSerializationService.CreateStore"/>.</exception>
		public override void Serialize(SerializationStore store, object value)
		{
			if (store == null)
				throw new ArgumentNullException("store");
			if (value == null)
				throw new ArgumentNullException("value");

			var serializationStore = store as FrameXmlSerializationStore;

			if (serializationStore == null)
				throw new BadSerializationStoreException(store);

			try
			{
				if (value is IFrameXmlComponent)
				{
					SerializationObject serializationObject = ((IFrameXmlComponent)value).SerializationObject;
					serializationStore.AddObject(serializer, serializationObject);
				}
			}
			catch (Exception e)
			{
				Trace.WriteLine(e);
				throw;
			}
		}

		/// <summary>
		/// Serializes the given object, accounting for default property values.
		/// </summary>
		/// <param name="store">The <see cref="T:System.ComponentModel.Design.Serialization.SerializationStore"/> to which the state of <paramref name="value"/> will be serialized.</param>
		/// <param name="value">The object to serialize.</param>
		/// <exception cref="T:System.ArgumentNullException">
		/// 	<paramref name="store"/> or <paramref name="value"/> is null.</exception>
		/// <exception cref="T:System.InvalidOperationException">
		/// 	<paramref name="store"/> is closed, or <paramref name="store"/> is not a supported type of serialization store. Use a store returned by <see cref="M:System.ComponentModel.Design.Serialization.CodeDomComponentSerializationService.CreateStore"/>.</exception>
		public override void SerializeAbsolute(SerializationStore store, object value)
		{
			if (store == null)
				throw new ArgumentNullException("store");

			throw new NotImplementedException();
		}

		/// <summary>
		/// Serializes the given member on the given object.
		/// </summary>
		/// <param name="store">The <see cref="T:System.ComponentModel.Design.Serialization.SerializationStore"/> to which the state of <paramref name="member"/> will be serialized.</param>
		/// <param name="owningObject">The object to which <paramref name="member"/> is attached.</param>
		/// <param name="member">A <see cref="T:System.ComponentModel.MemberDescriptor"/> specifying the member to serialize.</param>
		/// <exception cref="T:System.ArgumentNullException">
		/// 	<paramref name="store"/> or <paramref name="value"/> is null.</exception>
		/// <exception cref="T:System.InvalidOperationException">
		/// 	<paramref name="store"/> is closed, or <paramref name="store"/> is not a supported type of serialization store. Use a store returned by <see cref="M:System.ComponentModel.Design.Serialization.CodeDomComponentSerializationService.CreateStore"/>.</exception>
		public override void SerializeMember(SerializationStore store, object owningObject, MemberDescriptor member)
		{
			if (store == null)
				throw new ArgumentNullException("store");

			throw new NotImplementedException();
		}

		/// <summary>
		/// Serializes the given member on the given object, accounting for the default property value.
		/// </summary>
		/// <param name="store">The <see cref="T:System.ComponentModel.Design.Serialization.SerializationStore"/> to which the state of <paramref name="member"/> will be serialized.</param>
		/// <param name="owningObject">The object to which <paramref name="member"/> is attached.</param>
		/// <param name="member">The member to serialize.</param>
		/// <exception cref="T:System.ArgumentNullException">
		/// 	<paramref name="store"/> or <paramref name="value"/> is null.</exception>
		/// <exception cref="T:System.InvalidOperationException">
		/// 	<paramref name="store"/> is closed, or <paramref name="store"/> is not a supported type of serialization store. Use a store returned by <see cref="M:System.ComponentModel.Design.Serialization.CodeDomComponentSerializationService.CreateStore"/>.</exception>
		public override void SerializeMemberAbsolute(SerializationStore store, object owningObject, MemberDescriptor member)
		{
			if (store == null)
				throw new ArgumentNullException("store");

			throw new NotImplementedException();
		}
	}
}